using System;
using System.Collections.Generic;
using System.Text;
using JobShopModel.Model.FactoryLvl;
using JobShopModel.Model.JobLvl;
using JobShopModel.Model.Station;

//TODO: To Oscar, Need to find a way to get the total time waiting when you start moving an object ....

namespace JobShopModel.Model.Transport
{
    /// <summary>
    /// Transporter is the element that move the jobs between the stations
    /// Each jobs are retrieved from a FIFO list
    /// Element recorded:
    /// the total time a transporter is not moving <see cref=" TotalTimeWaiting"/>
    /// the total time a transporter is travelling <see cref=" TotalTimeTraveling"/>
    /// the total distance run by the transporter <see cref="TotalDistance"/>
    /// States:
    /// <see cref="TransportStates"/>
    /// </summary>
    public class Transporter
    {
        /// <summary>
        /// Expressed in Feet.Second^-1
        /// </summary>
        private Decimal speed;

        private Time totalTimeIddle;
        private Time totalTimeTraveling;

        private Time sIddle;

        //Transporter Utilisation
        private Decimal transpUtil;

        //Transporter average transp time
        private Time transAvgTime; 

        private int numberOfTransportation;
 
        private StationsName position;

        private TransportStates state;

        private Decimal totalDistance;
        private FIFOList list;
       
        /// <summary>
        /// Default Constructor
        /// </summary>
        public Transporter()
        {
            this.list = new FIFOList();
            this.state = TransportStates.IDDLE;
            this.sIddle = new Time();
            this.speed = new Decimal(5.0);
            this.totalTimeTraveling = new Time();
            this.totalTimeIddle = new Time();
            this.totalDistance = new Decimal();
            this.numberOfTransportation = 0;
            this.transAvgTime = new Time();
            this.transpUtil = (Decimal)0.0;
        }

        #region properties

        public int Transportations
        {
            get { return this.numberOfTransportation; }
        }

        public Decimal Utilisation
        {
            get { return this.transpUtil; }
        }

        public Time AvgTranspTime
        {
            get { return this.transAvgTime; }
        }

        /// <summary>
        /// State property
        /// Check the change of state depending on the logic.
        /// If illogical change, throw an exception 
        /// </summary>
        public TransportStates State
        {
            get { return this.state; }
          /*  set
            {
                if (this.state == TransportStates.IDDLE && value == TransportStates.MOVING)
                {
                    this.state = value;
                }
                else if (this.state == TransportStates.MOVING && value == TransportStates.IDDLE)
                {
                    this.state = value;
                }
                else
                {
                    throw new Exception("Illegal Operation: Can not go from state: " + this.state + " to state: " + value);
                }

            }*/
        }

        /// <summary>
        /// Total time the transporter is not moving
        /// </summary>
        public Time TotalTimeWaiting
        {
            get
            {
                return this.totalTimeIddle;
            }
        }

        /// <summary>
        /// TOtal time the transporter is moving
        /// </summary>
        public Time TotalTimeTraveling
        {
            get
            {
                return this.totalTimeTraveling;
            }
        }

        /// <summary>
        /// Total distance done by hte transporter
        /// </summary>
        public Decimal TotalDistance
        {
            get
            {
                return this.totalDistance;
            }
            set
            {
                if (value > 0)
                    this.totalDistance = value;
            }
        }

        /// <summary>
        /// Position of the transporter
        /// readonly property.
        /// To change its position, invoke <see cref="TravelTo"/>
        /// </summary>
        public StationsName Position
        {
            get { return this.position; }
        }

        #endregion properties

        /// <summary>
        /// Add a job to the fifo list.
        /// </summary>
        /// <param name="job">Job waiting ot be transported</param>
        public void AddJob(Job job)
        {
            list.push(job);
        }

        /// <summary>
        /// Get the first job on the FIFO list of the transporter
        /// </summary>
        /// <returns>Job to be transported</returns>
        public Job GetJob()
        {
            return list.pop();
        }

        /// <summary>
        /// Check if the FIFO list is empty
        /// </summary>
        /// <returns>true if empty, false otherwise</returns>
        public bool isEmpty()
        {
            return list.isempty();
        }

        /// <summary>
        /// Move the transporter from its position to an other
        /// Calcuate the time take
        /// Add the time taken to the statistics
        /// Update all the states
        /// </summary>
        /// <param name="end">End Position</param>
        /// <returns> Time taken by the transport</returns>
        public Time TravelTo(StationsName end)
        {
            //Get the distance to travel
            Decimal distance =(Decimal) Factory.getInstance().DistanceToStation(position, end);

            //Calculate the time in hours
            Decimal td = (distance / speed)/3600;
            Time t = new Time(0,td);

            //Update the states
            this.position = end;
            this.totalTimeTraveling += t;
            this.totalDistance += (Decimal)distance;

            return t;

        }

        public void StartIddle(Time start)
        {
            this.sIddle = start;
            this.state = TransportStates.IDDLE;
        }

        public void EndIddle(Time start)
        {
            Time timeIddle = start - sIddle;
            totalTimeIddle += timeIddle;
            this.state = TransportStates.MOVING;
        }

        public void UpdateStats()
        {
            this.numberOfTransportation++;
            //Update the statistic
            this.transpUtil = (this.totalTimeTraveling / Factory.getInstance().CurrentTime)*100;
            this.transAvgTime = this.totalTimeTraveling / numberOfTransportation;
        }
    }

    /// <summary>
    /// States of the Transporter
    /// </summary>
    public enum TransportStates 
    { 
        /// <summary>
        /// 
        /// </summary>
        MOVING,
        /// <summary>
        /// 
        /// </summary>
        BLOCKED,
        /// <summary>
        /// 
        /// </summary>
        IDDLE
    }

}
